package flex.cc.common.utils;

import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import flex.cc.base.exception.BusinessException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * JWT Token工具类
 *
 * @author yyf900524
 * @date 2019/08/13
 */
@Component
public class JwtUtils {

    private final Logger LOGGER = LoggerFactory.getLogger(JwtUtils.class);
    private static final String ACCESS_KEY = "accessKey";
	private static final String ACCESS_SECRET = "accessSecret";
	private static final String ACCESS_TOKEN = "accessToken";
	private static final String EXPIRATION_DATE = "expirationDate";
	private static Map<String,String> accessMap = new HashMap<>();
	
	static {
		accessMap.put("3EebOjYn", "6a2b9d82a4f1bebbfe40cc2e3e04ed1fc8b710bb");
		accessMap.put("GVCgH3XC", "d52c7535f857aa84110c5dad6ae6345fa2bf9bf0");
		accessMap.put("3cYxDIYq", "a60b7d5245e5e32750ac52001301a83ae4bd2019");
	}

    @Value("${sso.tokenHead:WebType }")
    private String tokenHead;

    @Value("${sso.secret:0ZLsy4Od597bS8Iu6u39F5N9Wumh6VFei1UHc0xDGN3gKHwlssvoTKyXNdNAKxnJltRcd2sv0PjWBDcLKk06e0xrM5HhAwkPr01w}")
    private String SECRET;

    private final Long overTimestamp = 7200000L;
    private final SignatureAlgorithm HS256 = SignatureAlgorithm.HS256;
    private final SignatureAlgorithm HS512 = SignatureAlgorithm.HS512;

    /**
     * 生成签名私钥Key
     *
     * @return
     */
    private SecretKey generalKey(SignatureAlgorithm sign) {
        byte[] encodedKey = DatatypeConverter.parseBase64Binary(SECRET);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, sign.getJcaName());
        return key;
    }

    /**
     * 生成随机字符
     *
     * @param length
     * @return
     */
    public String generalRandomString(int length) {
        SecureRandom secureRandom = new SecureRandom();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = secureRandom.nextInt(3);
            long result = 0;
            switch (number) {
                case 0:
                    result = Math.round(secureRandom.nextDouble() * 25 + 65);
                    sb.append(String.valueOf((char) result));
                    break;
                case 1:
                    result = Math.round(secureRandom.nextDouble() * 25 + 97);
                    sb.append(String.valueOf((char) result));
                    break;
                case 2:
                    sb.append(String.valueOf(secureRandom.nextInt(10)));
                    break;
                default:
                    break;
            }
        }
        return sb.toString();
    }

    /**
     * 生成过期时间
     *
     * @return
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + overTimestamp);
    }

    /**
     * 生成Jwt令牌，带过期时间
     * HS256
     *
     * @param claims
     * @return
     */
    public String generateHS256JwtHasExpiration(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(generateExpirationDate())
                .signWith(HS256, generalKey(HS256)).compact();
        return tokenHead + jwt;
    }

    /**
     * 生成Jwt令牌，带过期时间
     * HS512
     *
     * @param claims
     * @return
     */
    public String generateHS512JwtHasExpiration(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(generateExpirationDate())
                .signWith(HS512, generalKey(HS512)).compact();
        return tokenHead + jwt;
    }

    /**
     * 生成Jwt令牌
     * HS256
     *
     * @param claims
     * @return
     */
    public String generateHS256Jwt(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).signWith(HS256, generalKey(HS256)).compact();
        return tokenHead + jwt;
    }

    /**
     * 生成Jwt令牌
     * HS512
     *
     * @param claims
     * @return
     */
    public String generateHS512Jwt(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).signWith(HS512, generalKey(HS512)).compact();
        return tokenHead + jwt;
    }

    /**
     * 解析Jwt字符串
     * HS256
     *
     * @param jwt
     * @return
     */
    public Claims parseHS256Jwt(String jwt) {
        Claims claims = null;
        try {
            if (StringUtils.isNotBlank(jwt)) {
                claims = Jwts.parser().setSigningKey(generalKey(HS256)).parseClaimsJws(jwt.substring(tokenHead.length())).getBody();
            } else {
                LOGGER.warn("jwt为空");
            }
        } catch (ExpiredJwtException eje) {
            claims = eje.getClaims();
            LOGGER.error("jwt过期");
        } catch (Exception e) {
            claims = null;
            LOGGER.error("解析jwt异常：", e);
        }
        return claims;
    }

    /**
     * 解析Jwt字符串
     * HS512
     *
     * @param jwt
     * @return
     */
    public Claims parseHS512Jwt(String jwt) {
        Claims claims = null;
        try {
            if (StringUtils.isNotBlank(jwt)) {
                claims = Jwts.parser().setSigningKey(generalKey(HS512)).parseClaimsJws(jwt.substring(tokenHead.length())).getBody();
            } else {
                LOGGER.warn("jwt为空");
            }
        } catch (ExpiredJwtException eje) {
            claims = eje.getClaims();
            LOGGER.error("jwt过期");
        } catch (Exception e) {
            claims = null;
            LOGGER.error("解析jwt异常：", e);
        }
        return claims;
    }

    /**
     * 刷新Jwt字符串   带过期时间
     * HS256
     *
     * @param jwt
     * @return
     */
    public String refreshHS256JwtHasExpiration(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS256Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS256JwtHasExpiration(claims);
            }
        } catch (Exception e) {
            LOGGER.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 刷新Jwt字符串   带过期时间
     * HS512
     *
     * @param jwt
     * @return
     */
    public String refreshHS512JwtHasExpiration(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS512Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS512JwtHasExpiration(claims);
            }
        } catch (Exception e) {
            LOGGER.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 刷新Jwt字符串
     * HS256
     *
     * @param jwt
     * @return
     */
    public String refreshHS256Jwt(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS256Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS256Jwt(claims);
            }
        } catch (Exception e) {
            LOGGER.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 刷新Jwt字符串
     * HS512
     *
     * @param jwt
     * @return
     */
    public String refreshHS512Jwt(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS512Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS512Jwt(claims);
            }
        } catch (Exception e) {
            LOGGER.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 判断Token是否过期
     *
     * @param claims
     * @return false: 过期
     */
    public boolean isExpire(Claims claims) {
        Date expiration = claims.getExpiration();
        return expiration.before(new Date());
    }
    
    
    
    public Map<String, String> sign(String accessKey,String accessSecret) {
    	if(accessKey.equals("error")) {
    		throw new BusinessException(40001,"accessSecret错误，请确认accessSecret的正确性");
    	}
    	
    	Map<String,String>  result = new HashMap<>();
    	Map<String,Object> claims = new HashMap<>();
		claims.put(ACCESS_KEY, accessKey);
		claims.put(ACCESS_SECRET, accessSecret);
    	Date now = new Date(System.currentTimeMillis());
    	Date expirationDate = generateExpirationDate();
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(expirationDate)
                .signWith(HS256, generalKey(HS256)).compact();
        result.put(ACCESS_TOKEN, jwt);
        result.put(EXPIRATION_DATE, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(expirationDate));
        return result;
    }

	public void verify(String accessToken) {
		Claims claims = null;
		try {
			claims = Jwts.parser().setSigningKey(generalKey(HS256)).parseClaimsJws(accessToken).getBody();
		} catch (ExpiredJwtException eje) {
			throw new BusinessException(40014,"不合法的 accessToken ，请认真比对 accessToken 的有效性（如是否过期）");
		}
		
		String accessKey = claims.get(ACCESS_KEY).toString();
		String accessSecret = claims.get(ACCESS_SECRET).toString();
		if(null == accessMap.get(accessKey)) {
			throw new BusinessException(40001,"accessSecret错误，请确认accessSecret的正确性");
		}
		if(!accessSecret.equals(accessMap.get(accessKey))) {
			throw new BusinessException(40001,"accessSecret错误，请确认accessSecret的正确性");
		}
	}
    
    
}
